home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Cream of the Crop 1
/
Cream of the Crop 1.iso
/
PROGRAM
/
SYSTEM.ARJ
/
SYSTEMX.C
Wrap
C/C++ Source or Header
|
1992-05-23
|
13KB
|
443 lines
/*
NAME...
systemx - execute a program
SYNOPSIS...
int error;
char *cmd;
error = systemx(cmd);
NOTES...
The command line can have redirection and/or pipes. Temporary
files for pipes are created in the directory specified by the TEMP
environment variable, or else in the current directory.
For internal commands the shell (found via COMSPEC) is invoked.
(Intrinsics are assumed to be those of COMMAND.COM of MSDOS 3.3,
plus those added by MSDOS 5.0 if the current DOS version is 5.0 or
higher, or those of 4DOS if the COMSPEC includes the string
"4DOS".)
Otherwise, the PATH will be searched for the program. It will be
executed using the shell via the library function system() if it
turns out to be a batch file, or with spawnv() otherwise.
Any program returning a nonzero exit status will terminate a pipe
(unless the calling program sets the external variable
execution_continues nonzero). systemx returns the exit status of
the last program executed, or -1 if an error occurs. In the latter
case, errno is also set as follows:
ENOEXEC exec format error
ENOMEM out of memory
ENOENT no such file or directory
systemx is compatible with Ralf Brown's disk/XMS/EMS/ext-swapping
SPAWNO routines, which are recommended.
BUGS...
Could avoid big filename buffers by rearranging the user's
string (swap input and/or output file names to end and zero
terminate the substrings).
If stdin, stdout, or stderr haven't been redirected, their file
handles need not be saved. That would leave more handles
for the child process.
*/
#include <stdio.h>
#include <string.h>
#include <process.h>
#include <errno.h>
#include <io.h>
#include <sys\stat.h>
#include <fcntl.h>
#include <stdlib.h>
#define STANDARD_SHELL /* assume we know all the intrinsic commands */
#ifdef DEBUG
#define GRIPE(s) fprintf(stderr, s) /* error messages */
#define TR(m) printf m /* trace messages */
#else
#define GRIPE(s)
#define TR(m)
#endif
#define STDIN 0
#define STDOUT 1
#define STDERR 2
extern errno;
int execution_continues=0; /* calling program sets this nonzero
if execution of programs in a pipeline
should continue even if a program
returns a nonzero exit status */
static isfilename(int c)
{ return c && (isalnum(c) || strchr("\\:\._^$~!#%&-{}()@'`", c));
}
systemx(char *user_cmd)
{ int val, i, err, com, bat, ext, intrinsic;
int temphandle, inhandle, outhandle; /* file handles */
char c, *s, *t, *ptr, *tail, *cmd, *buf, *argv[3], *dp, *pp;
char oname[FILENAME_MAX]; /* name of output file */
char iname[FILENAME_MAX]; /* name of input file */
char directory[FILENAME_MAX]; /* name of one directory in the path */
char program[FILENAME_MAX]; /* name of program to execute
(no extension) */
char fullpath[FILENAME_MAX]; /* full pathname of program to execute */
char nothing[]="";
struct
{char res[21]; /* reserved */
char attr; /* file attribute */
long int datetime; /* date & time */
long int size; /* file size in bytes */
char name[13]; /* filename */
} dta; /* structure returned by findfirst() or findnext() */
enum {STANDARD, FILE, APPEND, PIPE} input, output;
input = STANDARD;
ptr = cmd = buf = strdup(user_cmd);
if(!cmd) {GRIPE("out of memory"); errno = ENOMEM; return -1;}
while(*ptr)
{/* handle one section of the pipeline */
output = STANDARD;
while(isspace(*ptr)) ptr++;
cmd = ptr;
while(isfilename(*ptr)) ptr++;
if(ptr == cmd)
{/* no program name present */
if(input == PIPE)
{GRIPE("program name missing");
errno = ENOEXEC; /* "Exec format error" */
return -1;
}
break;
}
/* parse up to the next '|' or the end of the string */
t = tail = ptr;
while(*ptr)
{c = *ptr++;
if(c == '"') /* argument delimited by quotes */
{*t++ = c;
while(*ptr)
{c = *t++ = *ptr++;
if(c == '\\' && *ptr == '"')
*t++ = *ptr++; /* escaped quote */
if(c == '"') break;
}
}
else if(c == '\\' && *ptr == '"')
{ /* escaped quote */
*t++ = c;
*t++ = *ptr++;
}
else if(c == '>')
{ /* redirected output */
output = FILE;
if(*ptr == '>') {ptr++; output = APPEND;}
s = oname;
while(isfilename(*ptr)) *s++ = *ptr++;
*s = 0;
}
else if(c == '<')
{ /* redirected input */
if(input != STANDARD)
{/* standard input has already been redirected */
GRIPE("doubly redirected input");
errno = ENOEXEC; /* "Exec format error" */
return -1;
}
input = FILE;
s = iname;
while(isfilename(*ptr)) *s++ = *ptr++;
*s = 0;
}
else if(c == '|')
{if(output != STANDARD)
{/* standard output has already been redirected */
GRIPE("doubly redirected output");
errno = ENOEXEC; /* "Exec format error" */
return -1;
}
output = PIPE;
break;
}
else *t++ = c;
}
*t = 0;
/*
assert:
*cmd = first character of program name
*tail = first character after program name
(file redirection arguments have been removed)
*ptr = first character after '|'
input = STANDARD, FILE, or PIPE
output = STANDARD, FILE, APPEND, or PIPE
If input == FILE then iname has the name of the input file.
If output==FILE or APPEND then oname has the name of the
output file.
*/
strncpy(program, cmd, tail-cmd);
program[tail-cmd] = 0;
pp = getenv("PATH");
if(!pp) pp = nothing;
dp = directory;
intrinsic = is_intrinsic(program);
TR(("%s is%s an intrinsic command\n", program, intrinsic?"":" not"));
com = ext = bat = 0;
while(!intrinsic)
{fullpath[0] = 0;
*dp = 0;
if(directory[0])
{strcat(fullpath, directory);
strcat(fullpath, "\\");
}
strcat(fullpath, program);
strcat(fullpath, ".*");
TR(("looking for <%s> \n", fullpath));
err = findfirst(fullpath, &dta, 0);
while(!err)
{
if(strstr(dta.name, ".COM")) {com = 1; break;}
else if(strstr(dta.name, ".EXE")) {ext = 1;}
else if(strstr(dta.name, ".BAT")) {bat = 1;}
err = findnext(&dta);
}
if(com||ext||bat) break;
if( !(*pp) ) /* can't find the program in the PATH */
{
#ifdef STANDARD_SHELL
GRIPE("program not found\n");
errno = ENOENT; /* "No such file or directory" */
return -1; /* return nonzero exit status */
#else
break; /* try the shell (which may implement
intrinsics other than those assumed here) */
#endif
}
dp = directory;
while(*pp) /* copy next directory in path */
{if(*pp == ';') {pp++; break;} /* skip ';' */
*dp++ = *pp++;
}
}
if(s = strchr(fullpath, '*'))
{if(com) strcpy(s, "COM");
else if(ext) strcpy(s, "EXE");
else if(bat) strcpy(s, "BAT");
}
switch(input)
{case PIPE:
case FILE:
temphandle = open(iname, O_RDONLY);
if(temphandle<0)
{GRIPE("file not found");
errno = ENOENT; /* "No such file or directory" */
return -1;
}
inhandle = dup(STDIN); /* create duplicate handle */
dup2(temphandle, STDIN); /* point STDIN to input file */
close(temphandle); /* release extra handle */
break;
case STANDARD:
;
}
flushall();
switch(output)
{case PIPE:
if(s = getenv("TEMP")) /* user specified tempfile directory */
{strncpy(oname, s, sizeof(oname) - 14);
s = oname + strlen(oname);
*s++ = '\\';
}
else s = oname;
tmpnam(s); /* manufacture name for pipe temporary */
case FILE:
unlink(oname);
case APPEND:
temphandle = open(oname, O_CREAT | O_RDWR, S_IREAD | S_IWRITE);
if(temphandle<0)
{GRIPE("file creation error");
errno = ENOENT; /* "No such file or directory" */
return -1;
}
outhandle = dup(STDOUT); /* create duplicate handle */
dup2(temphandle, STDOUT); /* redirect stdout to the file */
close(temphandle); /* release extra handle */
if(output == APPEND)
lseek(STDOUT, 0L, 2); /* seek to end of file */
break;
case STANDARD:
;
}
if(intrinsic || (!com && !ext)) /* including if it's a .BAT file */
{/* for an intrinsic command, we need COMMAND.COM after all */
TR(("calling system(\"%s\")\n", cmd));
val = system(cmd);
TR(("system() returns %d\n", val));
}
else
{
argv[0] = program; /* actually, spawnv() resets this */
argv[1] = tail;
argv[2] = NULL;
#ifdef DEBUG
{printf("calling spawnv(P_WAIT, \"%s\", argv) ", fullpath);
printf("\nwith argv = ");
for (i = 0; argv[i]; i++)
outstring(argv[i]);
printf("\n");
}
#endif
/*
The documentation of spawnv..() states that command arguments
should be in separate strings. One argument of the function is an
array of pointers to those strings. Here, I'm relying on an
undocumented feature of spawnv(): It parses the elements of argv[]
the same way command.com does. In particular, it will subdivide
arguments containing whitespace unless they are delimited by double
quotes
[foo bar] -> [foo] [bar]
it will remove the delimiting quotes
["foo"] -> [foo]
and it will remove escapes from quotes
[foo\"bar] -> [foo"bar]
The version in Borland's library and the one by Ralf Brown act the
same. (In fact, they handle one syntax error alike. If given a
string with an opening quote but no closing quote, they both
terminate the argument with a carriage return).
*/
val = spawnv(P_WAIT, fullpath, argv);
TR(("spawnv() returns %d\n", val));
}
flushall();
switch(input)
{case PIPE:
case FILE:
dup2(inhandle, STDIN); /* retore stdin to original source */
close(inhandle); /* release duplicate handle */
if(input == PIPE) unlink(iname);
break;
case STANDARD:
;
}
switch(output)
{case PIPE:
strcpy(iname, oname); /* save name of pipe temporary */
input = PIPE;
case FILE:
case APPEND:
dup2(outhandle, STDOUT); /* restore stdout to original file */
close(outhandle); /* release the duplicate handle */
break;
case STANDARD:
;
}
if(val && !execution_continues)
{if(output == PIPE) unlink(iname);
break;
}
}
free(buf);
return val;
}
char *(intrinsics_command[])=
{"BREAK", "CD", "CHDIR", "CLS", "COPY", "CTTY", "DATE", "DEL",
"DIR", "ECHO", "ERASE", "EXIT", "FOR", "IF", "MD", "MKDIR", "PATH",
"PAUSE", "PROMPT", "RD", "REM", "REN", "RENAME", "RMDIR", "SET",
"TIME", "TYPE", "VER", "VERIFY", "VOL", 0};
char *(intrinsics_4dos[])=
{"?", "ALIAS", "ATTRIB", "BEEP", "CALL", "CANCEL", "CDD", "CHCP",
"DESCRIBE", "DIRS", "ENDLOCAL", "ESET", "EXCEPT", "FREE", "GLOBAL",
"GOSUB", "GOTO", "HELP", "HISTORY", "INKEY", "INPUT", "KEYSTACK",
"LIST", "MEMORY", "MOVE", "POPD", "PUSHD", "QUIT", "RETURN",
"SCREEN", "SELECT", "SETDOS", "SETLOCAL", "SHIFT", "TEE", "TEXT",
"TIMER", "UNALIAS", "Y", 0};
char *(intrinsics_dos5[])=
{"CHCP", "LOADHIGH", "LH", 0}; /* these three were new with DOS 5.0 */
is_intrinsic(s) char *s;
{ int i;
extern unsigned char _osmajor;
char **sv=intrinsics_command;
for (i = 0; i < 2; i++)
{while(*sv) if(stricmp(s,*sv++)==0) return 1;
if(strstr(getenv("comspec"), "4DOS")) sv = intrinsics_4dos;
else if(_osmajor >= 5) sv = intrinsics_dos5;
else break;
}
return 0;
}
#ifdef DEBUG
/* execute systemx() from the command line */
main(int argc, char **argv)
{
char buf[200];
int i, value;
/* systemx("dir|sort>sorted"); */
if(argc < 2) {fprintf(stderr, "usage: systemx <prog> [options]"); exit(1);}
for (i = 0; i < argc; i++)
outstring(argv[i]);
putchar('\n');
buf[0] = 0;
for (i = 1; i < argc; i++) {strcat(buf, argv[i]); strcat(buf, " ");}
fprintf(stderr, "systemx(\"%s\")\n", buf);
value = systemx(buf);
fprintf(stderr, "systemx() returns %d\n", value);
if(value == -1)
{
fprintf(stderr, "errno = %d -> ", errno);
#define DISPLAY(name, val) if(errno==val) fprintf(stderr, name);
DISPLAY("E2BIG: Arg list too long ", 20);
DISPLAY("EINVAL: Invalid argument", 19);
DISPLAY("ENOENT: No such file or directory", 2);
DISPLAY("ENOEXEC: Exec format error", 21);
DISPLAY("ENOMEM: Not enough core", 8);
}
}
outstring(char *s)
{ int c;
printf(" <");
while(c = *s++)
if(isprint(c)) printf("%c", c);
else printf("\\%03o", c);
printf(">");
}
dump(s, n) char *s; int n;
{ int col=0;
while(n--)
{if(++col>16) {printf("\n"); col = 1;}
printf(" %02x", 0xff&*s++);
}
printf("\n");
}
#endif